(* Copyright (C) 2010 School of Software Engineering, 
 * University of Science and Technology of China.
 *
 * This code is released under a BSD-style license.
 * See the file LICENSE for details.
 *)

functor Jvm (S: JVM_STRUCTS): JVM = 
struct 

open S
open ByteCode
open Command CodeHeap

structure Store = Dict ()

exception Empty
exception OutOfBound

val pop = List.tl

val top = List.hd

val getTwoOp =
 fn [] => raise OutOfBound
  | [_] => raise OutOfBound
  | x1::x2::xs => (x1, x2, xs)

val getNthItem = List.nth

val fetchCode = getNthItem

val cache: Layout.t list ref = ref []

fun enter x = cache := x::(!cache)

fun execComm (c, stack, store, pc) =
    case c
     of Pop => (pop stack, store, pc+1)
      | Add => 
        let val (a, b, stack') = getTwoOp stack
        in  ((a+b)::b::stack', store, pc+1)
        end
      | Sub => 
        let val (a, b, stack') = getTwoOp stack
        in  ((a-b)::b::stack', store,pc+1)
        end
      | Swap => 
        let val (a, b, stack') = getTwoOp stack
        in  (b::a::stack', store, pc+1)
        end
      | Push i => (i::stack, store, pc+1)
      | Jump i => (stack, store, i)
      | Jeq i => 
        let val (a, b, _) = getTwoOp stack
        in  if a=b
            then (stack, store, i)
            else (stack, store, pc+1)
        end
      | Jlt i => 
        let val (a, b, _) = getTwoOp stack
        in  if a<b
            then (stack, store, i)
            else (stack, store, pc+1)
        end
      | Load i => 
        let val v = Store.lookup (store, i)
        in  (v::stack, store, pc+1)
        end
      | Store i => 
        let val v = top stack 
        in  (pop stack, Store.enter (store, i, v), pc+1)
        end
      | Stop => raise Fail "bug"

val rec spaces =
 fn 0 => ""
  | n => " "^(spaces (n-1))    

fun wrapper (layCode, max, code, stack, store, pc) =
    let val layPc = Layout.str (String.concat [spaces (pc*max), "^"])
        val layPc2 = Layout.str (String.concat [spaces (pc*max), "|"])
            
        val layStack = Layout.seq (List.map 
                                       (fn x => Layout.seq [Layout.str (Int.toString x), 
                                                            Layout.str ", "]) 
                                       stack)
        val layStore = Store.layout (store, Layout.str o Int.toString, Layout.str o Int.toString)
        val laySep = Layout.str "=================================="
        val _ = enter (Layout.align [Layout.str "codeHeap:",
                                     layCode, 
                                     layPc, 
                                     layPc2,
                                     Layout.str "stack:",
                                     layStack, 
                                     Layout.str "store:",
                                     layStore,
                                     laySep])
    in  execComm (code, stack, store, pc)
    end

fun loop (cs, stack, store, pc) =
    let val (layCode, max) = ByteCode.Command.layouts cs
        val code = fetchCode (cs, pc) 
    in  case code 
         of Stop => top stack
          | _ =>
            let val (newStack, newStore, newPc) = 
                    wrapper (layCode, max, code, stack, store, pc)
            in  loop (cs, newStack, newStore, newPc)
            end
    end

fun run (T cs) = loop (cs, [], Store.new {compare = Int.compare}, 0)

val run = Trace.trace ("Jvm.run", run, fn _ => Layout.empty (),
                    fn _ => Layout.align (List.rev (!cache)))

end
